package com.bifang.core.quartz.config;

import com.bifang.common.exception.AppException;
import com.bifang.common.vo.QuartzVO;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import org.apache.commons.lang3.StringUtils;
import org.quartz.CronScheduleBuilder;
import org.quartz.CronTrigger;
import org.quartz.Job;
import org.quartz.JobBuilder;
import org.quartz.JobDetail;
import org.quartz.JobKey;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.Trigger;
import org.quartz.TriggerBuilder;
import org.quartz.TriggerKey;
import org.quartz.TriggerUtils;
import org.quartz.impl.triggers.CronTriggerImpl;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

/**
 * 定时任务管理类
 *
 * @author chenzh
 * @editdate 2019-6-30
 */
@Component
public class QuartzManager {
    protected Logger logger = LoggerFactory.getLogger(this.getClass());
    /** 注入任务调度器 */
    @Autowired private Scheduler scheduler;

    private static String JOB_GROUP_NAME = "XY_JOBGROUP"; // 任务组
    private static String TRIGGER_GROUP_NAME = "XY_TRIGGERGROUP"; // 触发器组

    /**
     * 添加一个定时任务
     *
     * @param quartzVO 任务信息
     */
    public void addJob(QuartzVO quartzVO) {
        String jobName = quartzVO.getJobName(),
                jobGroup = quartzVO.getJobGroup(),
                cronExpression = quartzVO.getCronExpression(),
                jobDescription = quartzVO.getJobDescription(),
                triggerName = quartzVO.getTriggerName(),
                triggerGroupName = quartzVO.getTriggerGroup(),
                description = quartzVO.getDescription(),
                jobClassName = quartzVO.getJobClassName();
        if (StringUtils.isBlank(jobName)) {
            throw new AppException("任务名不能为空");
        }
        if (StringUtils.isBlank(cronExpression)) {
            throw new AppException("任务定时策略不能为空");
        }
        if (StringUtils.isBlank(jobClassName)) {
            throw new AppException("任务类路径不能为空");
        }
        if (StringUtils.isBlank(jobGroup)) {
            jobGroup = JOB_GROUP_NAME;
        }
        if (StringUtils.isBlank(triggerGroupName)) {
            triggerGroupName = TRIGGER_GROUP_NAME;
        }
        if (StringUtils.isBlank(description)) {
            description = jobDescription;
        }
        if (StringUtils.isBlank(triggerName)) {
            triggerName = jobName;
        }

        try {
            if (checkExists(jobName, jobGroup)) {
                throw new AppException(
                        String.format("Job已经存在, jobName:{%s},jobGroup:{%s}", jobName, jobGroup));
            }
            TriggerKey triggerKey = TriggerKey.triggerKey(triggerName, triggerGroupName);
            JobKey jobKey = JobKey.jobKey(jobName, jobGroup);
            // withMisfireHandlingInstructionDoNothing 第一次不执行
            CronScheduleBuilder scheduleBuilder =
                    CronScheduleBuilder.cronSchedule(cronExpression)
                            .withMisfireHandlingInstructionDoNothing();
            CronTrigger trigger =
                    TriggerBuilder.newTrigger()
                            .withIdentity(triggerKey)
                            .withDescription(description)
                            .withSchedule(scheduleBuilder)
                            .build();

            Class<? extends Job> clazz = (Class<? extends Job>) Class.forName(jobClassName);
            JobDetail jobDetail =
                    JobBuilder.newJob(clazz)
                            .withIdentity(jobKey)
                            .withDescription(jobDescription)
                            .build();
            if (!CollectionUtils.isEmpty(quartzVO.getParameter())) {
                jobDetail.getJobDataMap().putAll(quartzVO.getParameter());
            }

            scheduler.scheduleJob(jobDetail, trigger);
        } catch (AppException e) {
            throw e;
        } catch (Exception e) {
            logger.error("添加任务异常：", e);
            throw new AppException("添加任务异常：" + e.getMessage());
        }
    }
    /**
     * 修改定时任务
     *
     * @param quartzVO 任务信息
     */
    public void updateJob(QuartzVO quartzVO) {
        String jobName = quartzVO.getJobName(),
                jobGroup = quartzVO.getJobGroup(),
                cronExpression = quartzVO.getCronExpression(),
                jobDescription = quartzVO.getJobDescription(),
                triggerName = quartzVO.getTriggerName(),
                triggerGroupName = quartzVO.getTriggerGroup(),
                description = quartzVO.getDescription();
        if (StringUtils.isBlank(jobName)) {
            throw new AppException("任务名不能为空");
        }
        if (StringUtils.isBlank(cronExpression)) {
            throw new AppException("任务定时策略不能为空");
        }
        if (StringUtils.isBlank(jobGroup)) {
            jobGroup = JOB_GROUP_NAME;
        }
        if (StringUtils.isBlank(triggerGroupName)) {
            triggerGroupName = TRIGGER_GROUP_NAME;
        }
        if (StringUtils.isBlank(description)) {
            description = jobDescription;
        }
        if (StringUtils.isBlank(triggerName)) {
            triggerName = jobName;
        }

        try {
            if (!checkExists(jobName, jobGroup)) {
                throw new AppException(
                        String.format("Job不存在, jobName:{%s},jobGroup:{%s}", jobName, jobGroup));
            }
            TriggerKey triggerKey = TriggerKey.triggerKey(triggerName, triggerGroupName);
            JobKey jobKey = new JobKey(jobName, jobGroup);
            CronScheduleBuilder cronScheduleBuilder =
                    CronScheduleBuilder.cronSchedule(cronExpression)
                            .withMisfireHandlingInstructionDoNothing();
            CronTrigger cronTrigger =
                    TriggerBuilder.newTrigger()
                            .withIdentity(triggerKey)
                            .withDescription(description)
                            .withSchedule(cronScheduleBuilder)
                            .build();
            JobDetail jobDetail = scheduler.getJobDetail(jobKey);
            if (!CollectionUtils.isEmpty(quartzVO.getParameter())) {
                jobDetail.getJobDataMap().clear();
                jobDetail.getJobDataMap().putAll(quartzVO.getParameter());
            }
            jobDetail.getJobBuilder().withDescription(jobDescription);
            HashSet<Trigger> triggerSet = new HashSet<>();
            triggerSet.add(cronTrigger);
            scheduler.scheduleJob(jobDetail, triggerSet, true);
        } catch (AppException e) {
            throw e;
        } catch (Exception e) {
            logger.error("添加任务异常：", e);
            throw new AppException("添加任务异常：" + e.getMessage());
        }
    }
    /**
     * 删除一个任务
     *
     * @param quartzVO 任务
     */
    public void removeJob(QuartzVO quartzVO) {
        String jobName = quartzVO.getJobName(),
                jobGroup = quartzVO.getJobGroup(),
                triggerName = quartzVO.getTriggerName(),
                triggerGroupName = quartzVO.getTriggerGroup();
        if (StringUtils.isBlank(jobName)) {
            throw new AppException("任务名不能为空");
        }

        if (StringUtils.isBlank(jobGroup)) {
            jobGroup = JOB_GROUP_NAME;
        }
        if (StringUtils.isBlank(triggerGroupName)) {
            triggerGroupName = TRIGGER_GROUP_NAME;
        }
        if (StringUtils.isBlank(triggerName)) {
            triggerName = jobName;
        }
        try {
            TriggerKey triggerKey =
                    TriggerKey.triggerKey(triggerName, triggerGroupName); // 通过触发器名和组名获取TriggerKey
            JobKey jobKey = JobKey.jobKey(jobName, jobGroup); // 通过任务名和组名获取JobKey
            scheduler.pauseTrigger(triggerKey); // 停止触发器
            scheduler.unscheduleJob(triggerKey); // 移除触发器
            scheduler.deleteJob(jobKey); // 删除任务
        } catch (Exception e) {
            logger.error("删除任务异常：", e);
            throw new AppException("删除任务异常：" + e.getMessage());
        }
    }

    /** 启动所有定时任务 */
    public void startJobs() {
        try {
            if (!scheduler.isShutdown()) {
                scheduler.start();
            }
        } catch (Exception e) {
            throw new AppException("启动所有任务出错：" + e.getMessage());
        }
    }

    /** 关闭所有定时任务 */
    public void shutdownJobs() {
        try {
            if (!scheduler.isShutdown()) {
                scheduler.shutdown();
            }
        } catch (Exception e) {
            throw new AppException("关闭所有任务出错：" + e.getMessage());
        }
    }
    /** 暂停所有任务 */
    public void pauseAll() {
        try {
            scheduler.pauseAll();
        } catch (Exception e) {
            throw new AppException("暂停所有任务出错：" + e.getMessage());
        }
    }
    /** 恢复所有任务 */
    public void resumeAll() {
        try {
            scheduler.resumeAll();
        } catch (Exception e) {
            throw new AppException("恢复所有任务出错：" + e.getMessage());
        }
    }
    /**
     * @param quartzVO @Description:暂停一个任务
     */
    @Transactional(rollbackFor = Exception.class)
    public void pauseJob(QuartzVO quartzVO) {
        String jobName = quartzVO.getJobName(), jobGroup = quartzVO.getJobGroup();
        if (StringUtils.isBlank(jobName)) {
            throw new AppException("任务名不能为空");
        }
        if (StringUtils.isBlank(jobGroup)) {
            jobGroup = JOB_GROUP_NAME;
        }
        JobKey jobKey = JobKey.jobKey(jobName, jobGroup);
        try {
            if (checkExists(jobName, jobGroup)) {
                scheduler.pauseJob(jobKey);
            } else {
                throw new AppException("未找到任务");
            }
        } catch (AppException e) {
            throw e;
        } catch (Exception e) {
            throw new AppException("暂停任务出错：" + e.getMessage());
        }
    }
    /**
     * @param quartzVO @Description:暂停一个触发器
     */
    public void pauseTrigger(QuartzVO quartzVO) {
        String jobName = quartzVO.getJobName(),
                triggerName = quartzVO.getTriggerName(),
                triggerGroupName = quartzVO.getTriggerGroup();
        if (StringUtils.isBlank(jobName)) {
            throw new AppException("任务名不能为空");
        }
        if (StringUtils.isBlank(triggerGroupName)) {
            triggerGroupName = TRIGGER_GROUP_NAME;
        }
        if (StringUtils.isBlank(triggerName)) {
            triggerName = jobName;
        }
        TriggerKey triggerKey = TriggerKey.triggerKey(triggerName, triggerGroupName);

        try {
            if (checkExistsTigger(triggerName, triggerGroupName)) {
                scheduler.pauseTrigger(triggerKey);
            } else {
                throw new AppException("未找到任务");
            }
        } catch (AppException e) {
            throw e;
        } catch (Exception e) {
            throw new AppException("暂停触发器出错：" + e.getMessage());
        }
    }
    /**
     * @param quartzVO @Description:恢复一个任务(使用默认组名)
     */
    @Transactional(rollbackFor = Exception.class)
    public void resumeJob(QuartzVO quartzVO) {
        String jobName = quartzVO.getJobName(), jobGroup = quartzVO.getJobGroup();
        if (StringUtils.isBlank(jobName)) {
            throw new AppException("任务名不能为空");
        }
        if (StringUtils.isBlank(jobGroup)) {
            jobGroup = JOB_GROUP_NAME;
        }
        JobKey jobKey = JobKey.jobKey(jobName, jobGroup);
        try {
            scheduler.resumeJob(jobKey);
        } catch (Exception e) {
            throw new AppException("启动触任务出错：" + e.getMessage());
        }
    }
    /**
     * @param quartzVO @Description:恢复一个触发器
     */
    public void resumeTrigger(QuartzVO quartzVO) {
        String jobName = quartzVO.getJobName(),
                jobGroup = quartzVO.getJobGroup(),
                triggerName = quartzVO.getTriggerName(),
                triggerGroupName = quartzVO.getTriggerGroup();
        if (StringUtils.isBlank(jobName)) {
            throw new AppException("任务名不能为空");
        }
        if (StringUtils.isBlank(triggerGroupName)) {
            triggerGroupName = TRIGGER_GROUP_NAME;
        }
        if (StringUtils.isBlank(triggerName)) {
            triggerName = jobName;
        }
        TriggerKey triggerKey = TriggerKey.triggerKey(triggerName, triggerGroupName);

        try {
            if (checkExistsTigger(triggerName, triggerGroupName)) {
                scheduler.resumeTrigger(triggerKey);
            } else {
                throw new AppException("未找到任务");
            }
        } catch (AppException e) {
            throw e;
        } catch (Exception e) {
            throw new AppException("恢复触发器出错：" + e.getMessage());
        }
    }

    /**
     * @param quartzVO @Description:重置错误触发器
     */
    public void resetTriggerFromErrorState(QuartzVO quartzVO) {
        String jobName = quartzVO.getJobName(),
                jobGroup = quartzVO.getJobGroup(),
                triggerName = quartzVO.getTriggerName(),
                triggerGroupName = quartzVO.getTriggerGroup();
        if (StringUtils.isBlank(jobName)) {
            throw new AppException("任务名不能为空");
        }
        if (StringUtils.isBlank(triggerGroupName)) {
            triggerGroupName = TRIGGER_GROUP_NAME;
        }
        if (StringUtils.isBlank(triggerName)) {
            triggerName = jobName;
        }
        TriggerKey triggerKey = TriggerKey.triggerKey(triggerName, triggerGroupName);

        try {
            if (checkExistsTigger(triggerName, triggerGroupName)) {
                scheduler.resetTriggerFromErrorState(triggerKey);
            } else {
                throw new AppException("未找到任务");
            }
        } catch (AppException e) {
            throw e;
        } catch (Exception e) {
            throw new AppException("重置错误状态触发器出错：" + e.getMessage());
        }
    }
    /**
     * @param quartzVO @Description: 立即运行任务，这里的立即运行，只会运行一次，方便测试时用。
     * @date 2018年5月17日 上午10:03:26
     */
    public void triggerJob(QuartzVO quartzVO) {
        String jobName = quartzVO.getJobName(), jobGroup = quartzVO.getJobGroup();
        if (StringUtils.isBlank(jobName)) {
            throw new AppException("任务名不能为空");
        }
        if (StringUtils.isBlank(jobGroup)) {
            jobGroup = JOB_GROUP_NAME;
        }
        JobKey jobKey = JobKey.jobKey(jobName, jobGroup);
        try {
            scheduler.triggerJob(jobKey);
        } catch (Exception e) {
            logger.error("立即执行任务异常：", e);
            throw new AppException("立即执行任务异常：" + e.getMessage());
        }
    }
    /** 获取自定义参数 */
    public Map<String, Object> getJobParams(QuartzVO quartzVO) throws SchedulerException {
        String jobName = quartzVO.getJobName(), jobGroup = quartzVO.getJobGroup();
        if (StringUtils.isBlank(jobName)) {
            throw new AppException("任务名不能为空");
        }
        if (!checkExists(jobName, jobGroup)) {
            throw new AppException(
                    String.format("Job不存在, jobName:{%s},jobGroup:{%s}", jobName, jobGroup));
        }
        JobKey jobKey = new JobKey(jobName, jobGroup);
        JobDetail jobDetail = scheduler.getJobDetail(jobKey);
        return jobDetail.getJobDataMap();
    }
    /**
     * @param quartzVO @Description: 获取任务状态 NONE: 不存在 NORMAL: 正常 PAUSED: 暂停 COMPLETE:完成 ERROR : 错误
     *     BLOCKED : 阻塞
     * @date 2018年5月21日 下午2:13:45
     */
    public String getTriggerState(QuartzVO quartzVO) {
        String jobName = quartzVO.getJobName(),
                jobGroup = quartzVO.getJobGroup(),
                triggerName = quartzVO.getTriggerName(),
                triggerGroupName = quartzVO.getTriggerGroup();
        if (StringUtils.isBlank(jobName)) {
            throw new AppException("任务名不能为空");
        }

        if (StringUtils.isBlank(triggerGroupName)) {
            triggerGroupName = TRIGGER_GROUP_NAME;
        }
        if (StringUtils.isBlank(triggerName)) {
            triggerName = jobName;
        }

        TriggerKey triggerKey = TriggerKey.triggerKey(triggerName, triggerGroupName);
        String name = null;
        try {
            Trigger.TriggerState triggerState = scheduler.getTriggerState(triggerKey);
            name = triggerState.name();
        } catch (Exception e) {
            logger.error("获取任务状态异常：", e);
            throw new AppException("获取任务状态异常：" + e.getMessage());
        }
        return name;
    }

    /**
     * @param cron @Description:获取最近8次执行时间
     * @date 2018年5月24日 下午5:13:03
     */
    public List<String> getRecentTriggerTime(String cron) {
        List<String> list = new ArrayList<String>();
        try {
            CronTriggerImpl cronTriggerImpl = new CronTriggerImpl();
            cronTriggerImpl.setCronExpression(cron);
            // 这个是重点，一行代码搞定
            List<Date> dates = TriggerUtils.computeFireTimes(cronTriggerImpl, null, 8);
            SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            for (Date date : dates) {
                list.add(dateFormat.format(date));
            }

        } catch (Exception e) {
            logger.error("获取任务执行数据异常：", e);
            throw new AppException("获取任务执行数据异常：" + e.getMessage());
        }
        return list;
    }

    /**
     * 验证是否存在
     *
     * @param jobName
     * @param jobGroup
     * @throws SchedulerException
     */
    private boolean checkExists(String jobName, String jobGroup) throws SchedulerException {
        JobKey jobKey = JobKey.jobKey(jobName, jobGroup);
        return scheduler.checkExists(jobKey);
    }

    /**
     * 验证是否存在
     *
     * @param tiggerName
     * @param tiggerGroup
     * @throws SchedulerException
     */
    private boolean checkExistsTigger(String tiggerName, String tiggerGroup)
            throws SchedulerException {
        TriggerKey triggerKey = TriggerKey.triggerKey(tiggerName, tiggerGroup);
        return scheduler.checkExists(triggerKey);
    }
}
